/*
 * ths788.c
 *
 *  Created on: 2012. 2. 7.
 *      Author: blacklagoon
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sysdev.h>
#include <linux/syscalls.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>


static struct platform_driver ths788_driver =
{
		.probe   = NULL,
		.remove  = NULL,
		.suspend = NULL,
		.resume  = NULL,
		.driver  = {
				    .name = "ths788",
				    .owner= THIS_MODULE,
		},
};

/* THS788 Control pins */
#define Hclk 131
#define Hdat 130
#define Hstb 132
#define Hdir 136

/* Time Data read pins from THS788 */
#define Rclk 135
#define Rdat 134
#define Rstb 133

int ths788_open(void)
{
	return 0;
}

int ths788_release(void)
{
	return 0;
}

static unsigned char ths788_gpio_direct_input(char gpio)
{
	char buf=0;
	gpio_set_value(Hclk,0);
	buf=gpio_get_value(gpio);
	gpio_set_value(Hclk,1);
	return buf;
}

static int ths788_gpio_direct_output(char gpio, char dat)
{
	gpio_set_value(Hclk,0);
	gpio_set_value(gpio,dat&0x01 );
	gpio_set_value(Hclk,1);

	return 0;
}

/*Time result, 8 bit transper format*/
static int time_result_read()
{
	return 0;
}



ssize_t host_read(unsigned char address, unsigned int *dat)
{
	char loop=0;

	*dat = 0;

	gpio_set_value(Hstb,0);

	gpio_set_value(Hdir,0);

	ths788_gpio_direct_output(Hdat,1); //for read

	ths788_gpio_direct_output(Hdat,0); // dummy data. don't care

	loop=7;
	while(!(loop==255))
	{
		ths788_gpio_direct_output(Hdat,(address >> loop));
		//printk("%d",gpio_get_value(Hdat), loop);
		loop--;
	}
		//printk(" ");
	gpio_direction_input(Hdat);
	gpio_set_value(Hdat,0);
	gpio_set_value(Hdir,1);

	loop=16;
	while(!(loop==255))
	{
		//printk("%d", (ths788_gpio_direct_input(Hdat)&0x01) );
		*dat |=(ths788_gpio_direct_input(Hdat)&0x01) << loop;
		loop--;
	}
	*dat = *dat & 0xffff;

	gpio_set_value(Hstb,1);

	/* dummy clock. */
	ths788_gpio_direct_output(Hdat,0);
	ths788_gpio_direct_output(Hdat,0);


	gpio_direction_output(Hdat,0);
	gpio_set_value(Hdir,0);

	return 0;
}

ssize_t host_write(unsigned char address, unsigned int dat)
{
	char loop=0;

	gpio_set_value(Hstb,0);

	gpio_set_value(Hdir,0);

	ths788_gpio_direct_output(Hdat,0); //for write data

	ths788_gpio_direct_output(Hdat,0); // dummy data. but It's important to write data.

	loop=7;
	while(!(loop==255))
	{
		ths788_gpio_direct_output(Hdat,(address >> loop));
		//printk("%d",gpio_get_value(Hdat), loop);
		loop--;
	}

	loop=15;
	while(!(loop==255))
	{
		//printk("%d", (ths788_gpio_direct_input(Hdat)&0x01) );
		ths788_gpio_direct_output(Hdat,(dat >> loop));
		loop--;
	}

	ths788_gpio_direct_output(Hdat,1); // w0 data. but.. what is it?

	gpio_set_value(Hstb,1);

	/* dummy clock. */
	ths788_gpio_direct_output(Hdat,0);
	ths788_gpio_direct_output(Hdat,0);


	gpio_direction_output(Hdat,0);
	gpio_set_value(Hdir,0);

	return 0;
}

static void gpio_init(void)
{
	gpio_direction_input(Rclk);
	gpio_direction_input(Rdat);
	gpio_direction_input(Rstb);

	gpio_direction_output(Hclk,0);
	gpio_direction_output(Hstb,1);
	gpio_direction_output(Hdat,0);
	gpio_direction_output(Hdir,0);
}

static void gpio_giveup(void)
{
	gpio_free(Rclk);
	gpio_free(Rdat);
	gpio_free(Rstb);

	gpio_free(Hclk);
	gpio_free(Hstb);
	gpio_free(Hdat);
	gpio_free(Hdir);
}

static int ths788_setting(void)
{
	unsigned int dat=0,compare=0;;

	dat = 0b0010010011101000;

	host_write(0x80,dat);
	host_read(0x80,&compare);
	if(compare != dat)
		printk("Error : different register data (%04xh %04xh)\n",dat,compare);
	else
		printk("sysin : match register data (%04xh %04xh)\n",dat,compare);

	dat = 0b0000000000000011;

	compare=0;

	host_write(0x00,dat);
	host_read(0x00,&compare);
	if(compare != dat)
		printk("Error : different register data (%04xh %04xh)\n",dat,compare);
	else
		printk("sysin : match register data (%04xh %04xh)\n",dat,compare);

	compare=0;
	host_read(0x04,&compare);
	if( (compare & 0x0001) )
		printk("DLL is not locked in Channel A %xh\n",compare);
	else
		printk("DLL is locked in Channel A \n");

	compare=0;
	host_read(0x82,&compare);
	printk("THS788 Central control status : %04xh\n",compare);

	return 0;
}

/* Module init */
static __init ths788_init(void)
{

	platform_driver_register(&ths788_driver);

	printk("THS788 device driver init. \n");

	gpio_init();
	ths788_setting();

	return 0;
}

static __exit ths788_end(void)
{
	gpio_giveup();
	platform_driver_unregister(&ths788_driver);
	return 0;
}

module_init(ths788_init);
module_exit(ths788_end);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Motion Control LAB, Yonsei Univ.");
MODULE_DESCRIPTION("THS788 GPIO driver");
